package tuding.android.bigplanettracks.maps.loader;

import java.util.ArrayList;
import java.util.List;
//import java.util.concurrent.ExecutorService;
//import java.util.concurrent.Executors;

import tuding.android.bigplanettracks.maps.Handler;
import tuding.android.bigplanettracks.maps.RawTile;
//import tuding.android.bigplanettracks.maps.MarkerManager.Marker;
import tuding.android.bigplanettracks.maps.providers.MapStrategy;
import tuding.android.bigplanettracks.maps.PhysicMap;
import android.util.Log;

/**
 * Загрузчик тайлов с сервера
 * 
 * @author hudvin
 * 
 */
public class TileLoader implements Runnable {

	private static final int MAX_THREADS = 3;
	private static int current_thread_num = 0;
	private static final int MAX_LOADQUEUE_SIZE = PhysicMap.totalCells+1;

	private MapStrategy mapStrategy;

	private Handler handler;

//	private int counter = 0;
	
	public static boolean stop = false;
	
	private boolean useNet = true;

	//private static Stack<RawTile> loadQueue = new Stack<RawTile>();
	private static List<RawTile> loadQueue = new ArrayList<RawTile>();
	public static List<RawTile> loadingQueue = new ArrayList<RawTile>(MAX_THREADS);
	
	//private static ExecutorService mThreadPool = Executors.newFixedThreadPool(MAX_THREADS);
	
	/**
	 * Конструктор
	 * 
	 * @param handler
	 *            обработчик результата загрузки
	 */
	public TileLoader(Handler handler) {
		this.handler = handler;
		TileLoader.stop = false;
	}

	public void setMapStrategy(MapStrategy mapStrategy) {
		this.mapStrategy = mapStrategy;
	}

	public synchronized void setUseNet(boolean useNet) {
		this.useNet = useNet;
	}
	

	/**
	 * Добавляет в очередь на загрузку
	 * 
	 * @param tile
	 */
//	public synchronized void load(RawTile tile) {
//		addToQueue(tile);
//	}
    
    
    public synchronized void load(RawTile tile){
        if (mapStrategy!=null && useNet && tile != null){
        	// following check is performed in LocalStorageWrapper, therefore, every tile here is neither in DB nor in the queues
        	if(!loadQueue.contains(tile) && !loadingQueue.contains(tile)) {
        		//Log.i("LOADER","                                    need load tile: "+tile.x+" "+tile.y);
        	
		    	loadQueue.add(tile);
		    	if(loadQueue.size() > MAX_LOADQUEUE_SIZE) {
		    		loadQueue.remove(0);
		    	}
		    }
		    //Log.i("LOADER","current thread num is "+current_thread_num);
        	if(current_thread_num < MAX_THREADS && loadQueue.size() > 0) {
        		
        		RawTile rt = loadQueue.remove(0);
        		loadingQueue.add(rt); 
        		//Log.i("LOADER", "loadingQueue added: "+rt.x+" "+rt.y);
        		if(rt != null) {
	        		Thread get_tile_th = new ThreadLoader(rt);
	        		get_tile_th.start();
	        		current_thread_num++;
        		} else {
        			//Log.e("LOADER", "loadQueue contains null tile?");
        		}
        	}
    	}
    }/*
	public synchronized void load(RawTile tile) {
		if(mapStrategy!=null && useNet) {
		    if(!loadQueue.contains(tile)) {
		    	Log.i("LOADER","loadQ does not contains the tile: "+tile.x+" "+tile.y);
		    	addToQueue(tile);
		    }
	    	RawTile rt = getFromQueue();
	    	if (null != rt) {
	    		Log.i("LOADER","threadpool execute load the tile: "+tile.x+" "+tile.y);
	    		mThreadPool.execute(new ThreadLoader(rt));
	    	}
		}
	}*/



//	public synchronized void tileLoaded(RawTile tile, byte[] data) {
//		if (data != null) {
//			handler.handle(tile, data);
//		}
//		counter--;
//	}
    public synchronized void tileLoaded(RawTile tile, byte[] data) {
        if(data != null){
        	//synchronized(loadQueue) {
        	{
	        	//Log.i("LOADER","Tile loaded and loadQueue.size is "+loadQueue.size()+" loadingQueue size is "+loadingQueue.size()+" tile is: "+tile.x+" "+tile.y);
	        	if(loadQueue.size()>0) {
	        		//Log.i("LOADER","         start a new thread! ");
	        		RawTile rt = loadQueue.remove(0);
	        		loadingQueue.add(rt);
	        		//Log.i("LOADER", "loadingQueue added: "+rt.x+" "+rt.y);
	        		if(rt != null) {
	        			Thread get_tile_th = new ThreadLoader(rt);
	        			get_tile_th.start();
	        		} else {
	        			Log.e("LOADER", "loadQueue contains a null tile!");
	        		}
	        		current_thread_num++;
	        	} else {
	        		//current_thread_num--; // timeout should alse decrease thread count
	        		//Log.i("LOADER","tile loaded current thread num is "+current_thread_num);
	        	}
        	}
            handler.handle(tile, data);
        } else { // loading exception, the tile is not loaded from internet, we need clear it from queue for next try.
        	Log.w("LOAD", "tileLoaded with null data");
        	try {
        		loadingQueue.remove(tile);
        	}catch (Exception e) {
        		e.printStackTrace();
        	}
        }
        current_thread_num--;
    }

    /*
     * We donot want there is always running thread to consume power.
     */
	public void run() {
	   /*
		while (true) {
			try {
				if (mapStrategy!=null && useNet && counter < MAX_THREADS && loadQueue.size() > 0) {
					RawTile rt = getFromQueue();
//					Log.i("LOADER", "Tile " + rt + " start loading");
					if (null != rt) {
						counter++;
						mThreadPool.execute(new ThreadLoader(rt));
					}
				}
				Thread.sleep(100);
				if (stop) { // stop this thread after exiting the application
					break;
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		*/
	}

	private class ThreadLoader extends BaseLoader {

		public ThreadLoader(RawTile tile) {
			super(tile);
		}

		@Override
		protected MapStrategy getStrategy() {
			return TileLoader.this.mapStrategy;
		}

		@Override
		protected void handle(RawTile tile, byte[] data, int meta) {
//			System.out.println("handle " + tile);
			TileLoader.this.tileLoaded(tile, data);
		}

	}

}
